package cn.net.xyan.easy.spring.jpa.support;

import cn.net.xyan.easy.spring.core.exception.StatusAndMessageError;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.context.EnvironmentAware;
import org.springframework.context.ResourceLoaderAware;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.env.Environment;
import org.springframework.core.io.ResourceLoader;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.data.jpa.repository.config.JpaRepositoryConfigExtension;
import org.springframework.data.repository.config.AnnotationRepositoryConfigurationSource;
import org.springframework.data.repository.config.RepositoryConfigurationDelegate;
import org.springframework.data.repository.config.RepositoryConfigurationExtension;
import org.springframework.data.repository.config.RepositoryConfigurationUtils;
import org.springframework.util.Assert;

import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;

/**
 * Created by xiashenpin on 16/1/16.
 */
public class EasyJpaRepositoriesRegistrar implements ImportBeanDefinitionRegistrar,
        ResourceLoaderAware, EnvironmentAware {

    private ResourceLoader resourceLoader;
    private Environment environment;

    public void registerBeanDefinitions(AnnotationMetadata annotationMetadata, BeanDefinitionRegistry registry) {
        //super.registerBeanDefinitions(annotationMetadata, registry);

        Assert.notNull(getResourceLoader(), "ResourceLoader must not be null!");
        Assert.notNull(annotationMetadata, "AnnotationMetadata must not be null!");
        Assert.notNull(registry, "BeanDefinitionRegistry must not be null!");

        // Guard against calls for sub-classes
        if (annotationMetadata.getAnnotationAttributes(getAnnotation().getName()) == null) {
            return;
        }

       // AnnotationRepositoryConfigurationSource configurationSource = /*new AnnotationRepositoryConfigurationSource(
          //      annotationMetadata, getAnnotation(), getResourceLoader(), getEnvironment());*/
         //       null;
        try {
            AnnotationRepositoryConfigurationSource configurationSource = createRepositoryConfigurationSource(annotationMetadata);
            RepositoryConfigurationExtension extension = getExtension();
            RepositoryConfigurationUtils.exposeRegistration(extension, registry, configurationSource);

            RepositoryConfigurationDelegate delegate = new RepositoryConfigurationDelegate(configurationSource, getResourceLoader(),
                    getEnvironment());

            delegate.registerRepositoriesIn(registry, extension);
        } catch (Exception e) {
            throw new StatusAndMessageError(-1,e);
        }
    }

    protected AnnotationRepositoryConfigurationSource createRepositoryConfigurationSource(AnnotationMetadata annotationMetadata) throws Exception {
        Class<? extends AnnotationRepositoryConfigurationSource> cls = getRepositoryConfigurationSourceType();
        Constructor<? extends AnnotationRepositoryConfigurationSource> constructor =  cls.getConstructor(AnnotationMetadata.class,Class.class,ResourceLoader.class,Environment.class);
        return constructor.newInstance(annotationMetadata,getAnnotation(),getResourceLoader(),getEnvironment());
    }

    protected  Class<? extends AnnotationRepositoryConfigurationSource> getRepositoryConfigurationSourceType(){
        return EasyAnnotationRepositoryConfigureationSource.class;
    }

    protected Class<? extends Annotation> getAnnotation() {
        return EnableEasyJpaRepositories.class;
    }


    protected RepositoryConfigurationExtension getExtension() {
        return new JpaRepositoryConfigExtension();
    }

    protected ResourceLoader getResourceLoader() {
        return resourceLoader;
    }

    public void setResourceLoader(ResourceLoader resourceLoader) {
        this.resourceLoader = resourceLoader;
    }

    protected Environment getEnvironment() {
        return environment;
    }

    public void setEnvironment(Environment environment) {
        this.environment = environment;
    }
}
